#include "decal.h"
#include "backdrop.h"

#include "util/mmgr.h"
#include "util/common_macros.h"

using namespace ui;
using namespace backdrop;

Decal::Decal(backdrop::Backdrop* pBackdrop) : m_pBackdrop(pBackdrop),
	m_DecalType(DECAL_BACKGROUND),
	m_IsClipped(false),
	m_IsVisible(true),
	m_ParallaxFactor(1.0f),
	m_Color(0xFFFFFFFF)
{
}

Decal::~Decal()
{
}

bool Decal::CreateDecal(const std::string& File)
{
	std::string DecalType;
	int From, To;
	bool bRet = false;

	m_DecalName = File;
	From = (int)m_DecalName.find_first_of("[");
	To   = (int)m_DecalName.find_last_of("]");

	if ((From > 0) && ((To - From) > 1)) {
		DecalType = m_DecalName.substr(From + 1, To - From - 1);
		m_DecalName.resize(From);
		m_DecalType = GetTypeFromString(DecalType);
	}

	if (bRet = m_Sprite.Create(m_DecalName))
		m_Size = m_Sprite.m_Size;

	return bRet;
}

void Decal::SetPos(const math::Vec2& Pos)
{
	m_StartPos = m_Pos = Pos;
}

void Decal::SetSize(const math::Vec2& Size)
{
	m_Size = Size;
}

bool Decal::Move(const math::Vec2& Offset)
{
	math::Vec2 Dest;
	math::Vec2 Size = GetSize();
	math::Vec2 Extent(m_pBackdrop->GetExtentW(), m_pBackdrop->GetExtentH());

	m_IsClipped = false;
	Dest = m_Pos + Offset;

	if (WrapX() || WrapY())
	{
		if (Offset.x > 0.0f)
		{
			if (Dest.x > Extent.x) { Dest.x = 0.0f; m_IsClipped = true; }
			if ((Dest.x + Size.x) > Extent.x) { m_IsClipped = true; }
		}
		else if (Offset.x < 0.0f)
		{
			if (Dest.x < 0.0f) { Dest.x = Extent.x; m_IsClipped = true; }
			if ((Dest.x + Size.x) > Extent.x) { m_IsClipped = true; }
		}

		if (Offset.y > 0.0f)
		{
			if (Dest.y > Extent.y) { Dest.y = 0.0f; m_IsClipped = true; }
			if ((Dest.y + Size.y) > Extent.y) { m_IsClipped = true; }
		}
		else if (Offset.y < 0.0f)
		{
			if (Dest.y < 0.0f) { Dest.y = Extent.y; m_IsClipped = true; }
			if ((Dest.y + Size.y) > Extent.y) { m_IsClipped = true; }
		}
	}
	else
	{
		if ((Offset.x > 0.0f) && (Dest.x > Extent.x)) { Dest.x = -Size.x; m_IsClipped = true; }
		else if ((Offset.x < 0.0f) && ((Dest.x  + Size.x) < 0.0f)) { Dest.x = Extent.x + Size.x; m_IsClipped = true; }

		if ((Offset.y > 0.0f) && (Dest.y > Extent.y)) { Dest.y = -Size.y; m_IsClipped = true; }
		else if ((Offset.y < 0.0f) && ((Dest.y + Size.y) < 0.0f)) { Dest.y = Extent.y + Size.y; m_IsClipped = true; }
	}

	m_Pos = Dest;
	return m_IsClipped;
}

bool Decal::Tick(float dt)
{
	if (m_IsVisible && m_Sprite.IsValid())
		return m_Sprite.Tick(dt);

	return true;
}

bool Decal::Render()
{
	if (!m_IsVisible || !m_Sprite.IsValid()) return false;

	bool bWraparound = WrapX() || WrapY();
	math::Vec2 Size = GetSize();
	math::Vec2 Pos = m_Pos - (m_pBackdrop->GetOffset() * m_ParallaxFactor);

	if (((Pos.x >= -Size.x) && (Pos.x <= 1.0f)) && ((Pos.y >= -Size.y) && (Pos.y <= 1.0f))) {
		m_Sprite.Setup(Pos, Size, 0.0f, 1.0f, m_Color);
		RenderSprite();
	} else if (bWraparound) {
		if (WrapX()) {
			while (Pos.x < 0.0f) Pos.x += 1.0f;
			while (Pos.x > 1.0f) Pos.x -= 1.0f;
		}
		if (WrapY()) {
			while (Pos.y < 0.0f) Pos.y += 1.0f;
			while (Pos.y > 1.0f) Pos.y -= 1.0f;
		}
		m_Sprite.Setup(Pos, Size, 0.0f, 1.0f, m_Color);
		RenderSprite();
	}

	if (bWraparound || m_IsClipped)
	{	
		bool DoWrap = false;
		math::Vec2 Clone = Pos;

		if (WrapX()) {
			if ((Pos.x > 0.0f) && ((Pos.x + Size.x) > 1.0f)) {
				Clone.x -= Size.x;
				DoWrap = true;
			} else if (bWraparound && (Pos.x < 0.0)) {
				Clone.x += Size.x;
				DoWrap = true;
			}
		}

		if (WrapY()) {
			if ((Pos.y > 0.0f) && ((Pos.y + Size.y) > 1.0f)) {
				Clone.y -= Size.y;
				DoWrap = true;
			} else if (bWraparound && (Pos.y < 0.0f)) {
				Clone.y += Size.y;
				DoWrap = true;
			}
		}

		if (DoWrap) {
			m_Sprite.Setup(Clone, Size, 0.0f, 1.0f, m_Color);
			RenderSprite();
		}
	}

	return true;
}

bool Decal::HitTest(const math::Vec2& Pos)
{
	if (!m_IsVisible || !m_Sprite.IsValid())
		return false;

	return m_Sprite.IsInside(Pos);
}

void Decal::RenderSprite()
{
	if ((m_Sprite.m_Pos.x > 1.0f) || (m_Sprite.m_Pos.x < -m_Sprite.m_Size.x) ||
		(m_Sprite.m_Pos.y > 1.0f) || (m_Sprite.m_Pos.y < -m_Sprite.m_Size.y))
		return;

	if ((m_Sprite.m_Pos.x >= 0.0f) && (m_Sprite.m_Pos.x <= (1.0f - m_Sprite.m_Size.x)) &&
		(m_Sprite.m_Pos.y >= 0.0f) && (m_Sprite.m_Pos.y <= (1.0f - m_Sprite.m_Size.y)))
	{
		m_Sprite.Render();
	}
	else
	{
		math::Vec2 ClipMin, ClipMax;

		ClipMin.x = UTIL_HIGH(0.0f, m_Sprite.m_Pos.x);
		ClipMin.y = UTIL_HIGH(0.0f, m_Sprite.m_Pos.y);
		ClipMax.x = UTIL_LOW(1.0f, (m_Sprite.m_Pos.x + m_Sprite.m_Size.x));
		ClipMax.y = UTIL_LOW(1.0f, (m_Sprite.m_Pos.y + m_Sprite.m_Size.y));

		m_Sprite.RenderClipped(ClipMin, ClipMax);
	}
}
